﻿using MapEditor.Model;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using TileMapTool;

namespace MapEditor
{
    public enum MapOptionStates
    {
        Create = 0,
        Edit = 1,
        CreateMultiItem = 2
    }

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private const double objectDistance = 0.001;

        private List<MapGameItem> MapObjects;

        private bool IsMouseDown = false;
        private bool isFirstMouseDown = true;
        private System.Windows.Point CurrentPointInMap;
        private System.Windows.Point PrevPointInMap;
        private System.Windows.Shapes.Rectangle CurrentRectInMap;
        private System.Windows.Shapes.Rectangle CurrentGameItem;

        private GameItem SelectedGameItem;

        private double MapHeight;
        private double MapWidth;
        private bool existPlayer = false;

        private MapOptionStates MapOptionState;

        //Create multi item
        private bool isCreateMultiItem = false;
        private System.Windows.Point origMouseDownPoint;


        public MainWindow()
        {
            InitializeComponent();

            //Set map option state
            cbbOption.SelectedIndex = 0;
            MapOptionState = MapOptionStates.Create;

            //Set default map size
            MapHeight = cvsMap.Height;
            MapWidth = cvsMap.Width;
            MapObjects = new List<MapGameItem>();

            //Get current game selected item
            System.Windows.Shapes.Rectangle rec = new System.Windows.Shapes.Rectangle();
            lGameItem.SelectedIndex = 1;
            SelectedGameItem = (GameItem)lGameItem.SelectedItem;
            CurrentGameItem = RectangleFactory.getDefaultRect(SelectedGameItem.ImageObject);


        }

        #region Load and save map file
        //Load Map from file to edit
        private void btnLoadFile_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Title = "Open Map file To Edit";
            dialog.Filter = "TXT files|*.txt";
            if ((bool)dialog.ShowDialog())
            {
                BitmapService service = new BitmapService(32, 32);
                if (File.Exists(dialog.FileName.Replace(".txt", ".png")))
                {
                    Bitmap bitmap = service.LoadMapFromFile(dialog.FileName, dialog.FileName.Replace(".txt", ".png"));
                    //Bitmap bitmap = new Bitmap(dialog.FileName);
                    ImageBrush ib = new ImageBrush();
                    ImageSourceConverter c = new ImageSourceConverter();

                    using (MemoryStream memory = new MemoryStream())
                    {
                        bitmap.Save(memory, ImageFormat.Png);
                        memory.Position = 0;
                        BitmapImage bmImage = new BitmapImage();
                        bmImage.BeginInit();
                        bmImage.StreamSource = memory;
                        bmImage.CacheOption = BitmapCacheOption.OnLoad;
                        bmImage.EndInit();

                        ib.ImageSource = bmImage;
                        cvsMap.Width = ib.ImageSource.Width;
                        cvsMap.Height = ib.ImageSource.Height;
                        MapWidth = cvsMap.Width;
                        MapHeight = cvsMap.Height;
                    }

                    cvsMap.Background = ib;
                    txtMapWidth.Text = bitmap.Width.ToString();
                    txtMapHeight.Text = bitmap.Height.ToString();
                    btnSubmit.Visibility = System.Windows.Visibility.Hidden;
                }
                else
                {
                    MessageBox.Show("This is invalib file, Please get another file!");
                }
                
            }

            //cvsDragSelection.Margin = borderMap.Margin;
        }


        /// <summary>
        /// Save map to file
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSaveMap_Click(object sender, RoutedEventArgs e)
        {
            SaveFileDialog saveDialog = new SaveFileDialog();
            saveDialog.Title = "Save Game Map";
            saveDialog.Filter = "TXT files|*.txt"; ;
            if ((bool)saveDialog.ShowDialog())
            {
                string fileName = saveDialog.FileName;
                MapService.MapService.MapWidth = MapWidth;
                MapService.MapService.MapHeight = MapHeight;
                MapService.MapService.CreateGameMap(MapObjects, fileName);

                MessageBox.Show("Success");
            }
        }

        #endregion


        #region Edit map
        private void lGameItem_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            SelectedGameItem = (GameItem)lGameItem.SelectedItem;

        }

        private void cvsMap_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            IsMouseDown = true;
            if (cvsMap.Children.Count > 1)
            {
                HitTestResult target = VisualTreeHelper.HitTest(cvsMap, e.GetPosition(cvsMap));
                var ob = target.VisualHit;
            }
            switch (MapOptionState)
            {
                case MapOptionStates.Create:
                    CurrentGameItem = RectangleFactory.getDefaultRect(SelectedGameItem.ImageObject);
                    if (CurrentGameItem != null)
                    {
                        if (existPlayer == true && SelectedGameItem.Type == 0)
                        {
                            MessageBox.Show("One player only, please!");
                        }
                        if (SelectedGameItem.Type == 0)
                        {
                            existPlayer = true;
                        }
                        if (SelectedGameItem.Type == 11)
                        {
                            createBridge(new System.Windows.Point(e.GetPosition(cvsMap).X, e.GetPosition(cvsMap).Y), lGameItem.SelectedIndex);
                        }
                        else
                        {
                            AddRectangleToCanvas(CurrentGameItem, SelectedGameItem.Type, new System.Windows.Point(e.GetPosition(cvsMap).X, e.GetPosition(cvsMap).Y));
                        }
                    }
                    break;
                case MapOptionStates.CreateMultiItem:
                    origMouseDownPoint = e.GetPosition(cvsMap);
                    //this.CaptureMouse();

                    e.Handled = true;
                    break;
                case MapOptionStates.Edit:
                    if (isFirstMouseDown)
                    {
                        if (CurrentRectInMap != null)
                        {
                            CurrentRectInMap.Stroke = System.Windows.Media.Brushes.Transparent;
                        }
                        CurrentRectInMap = GetCurrentSelectedIlementInCanvas(e.GetPosition(cvsMap));
                        if (CurrentRectInMap != null)
                        {
                            CurrentRectInMap.Stroke = System.Windows.Media.Brushes.Blue;
                            btndelete.Opacity = 1;
                        }

                    }
                    break;
                default:
                    break;
            }

        }

        private void cvsMap_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (isCreateMultiItem)
            {
                isCreateMultiItem = false;
                ApplyDragSelectionRect();

                e.Handled = true;
            }

            if (IsMouseDown)
            {
                IsMouseDown = false;
                isFirstMouseDown = true;

                this.ReleaseMouseCapture();

                e.Handled = true;
            }
        }

        private void cvsMap_MouseMove(object sender, MouseEventArgs e)
        {
            //
            if (MapOptionState == MapOptionStates.CreateMultiItem)
            {
                if (isCreateMultiItem)
                {
                    System.Windows.Point curMouseDownPoint = e.GetPosition(cvsMap);
                    updateSelectionRect(origMouseDownPoint, curMouseDownPoint);

                    e.Handled = true;
                }
                else if (IsMouseDown)
                {
                    System.Windows.Point curMouseDownPoint = e.GetPosition(cvsMap);
                    var dragDeltal = curMouseDownPoint - origMouseDownPoint;
                    double dragDistance = Math.Abs(dragDeltal.Length);
                    if (dragDistance > 5)
                    {
                        isCreateMultiItem = true;
                        InitDragSelectionRect(origMouseDownPoint, curMouseDownPoint);
                    }

                    e.Handled = true;
                }

            }
            else
            {
                if (IsMouseDown)
                {
                    if (isFirstMouseDown)
                    {
                        //Get current selected item in mouse position
                        CurrentPointInMap = e.GetPosition(this);
                        PrevPointInMap = CurrentPointInMap;
                        CurrentRectInMap = GetCurrentSelectedIlementInCanvas(e.GetPosition(cvsMap));
                        isFirstMouseDown = false;
                    }
                    else
                    {
                        PrevPointInMap = CurrentPointInMap;
                        CurrentPointInMap = e.GetPosition(this);
                    }
                    if (CurrentRectInMap == null)
                    {
                        //Scroll canvas if there are no selected rectangle
                        if (!isFirstMouseDown)
                        {
                            ScrollCanvasByDrag(CurrentPointInMap, PrevPointInMap);
                        }
                        return;
                    }
                    setRectanglePositionInCanvas(CurrentRectInMap, CurrentPointInMap, PrevPointInMap);
                }
            }
        }

        /// <summary>
        /// Change Map size
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSubmit_Click(object sender, RoutedEventArgs e)
        {
            double newHeight = 0;
            if (!string.IsNullOrEmpty(txtMapHeight.Text.ToString()))
            {
                newHeight = Convert.ToDouble(txtMapHeight.Text.ToString());
            }
            double newWidth = 0;
            if (1 == 1)
            {
                newWidth = Convert.ToDouble(txtMapWidth.Text.ToString());
            }

            if (newHeight != MapHeight)
            {
                if (cvsMap.Children.Count > 0)
                {
                    cvsMap.Children.RemoveRange(0, cvsMap.Children.Count);
                }
                cvsMap.Height = newHeight;
                MapHeight = newHeight;
            }
            if (newWidth != MapWidth)
            {
                if (cvsMap.Children.Count > 0)
                {
                    cvsMap.Children.RemoveRange(0, cvsMap.Children.Count);
                }
                cvsMap.Width = newWidth;
                MapWidth = newWidth;
            }
        }

        /// <summary>
        /// Change Map option states
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cbbOption_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (cbbOption.SelectedIndex == 0)
            {
                MapOptionState = MapOptionStates.Create;
            }
            else if (cbbOption.SelectedIndex == 2)
            {
                MapOptionState = MapOptionStates.Edit;
            }
            else
            {
                MapOptionState = MapOptionStates.CreateMultiItem;
            }
        }
        #endregion

        #region Canvas option
        private void ScrollCanvasByDrag(System.Windows.Point CurrentPoint, System.Windows.Point PrevPoint)
        {
            double deltaX = CurrentPoint.X = PrevPoint.X;
            double deltaY = CurrentPoint.Y - PrevPoint.Y;

            scrollMap.ScrollToHorizontalOffset(scrollMap.HorizontalOffset - deltaX);
            scrollMap.ScrollToVerticalOffset(scrollMap.VerticalOffset - deltaY);
        }

        private bool AddRectangleToCanvas(System.Windows.Shapes.Rectangle Rectangle, int Type, System.Windows.Point Position)
        {
            if (Rectangle == null)
            {
                return false;
            }

            Model.Rect rect = new Model.Rect() { X = Position.X, Y = Position.Y, Width = Rectangle.Width, Height = Rectangle.Height };
            //check if rect out of map then stop create
            if (!CheckRectInvalibPosition(rect))
            {
                return false;
            }

            //Add rectangle to map
            cvsMap.Children.Add(Rectangle);
            Canvas.SetLeft(Rectangle, Position.X - Rectangle.Width / 2);
            Canvas.SetTop(Rectangle, Position.Y - Rectangle.Height / 2);
            MapObjects.Add(new MapGameItem(Type, Rectangle));
            return true;
        }

        private bool CheckRectInvalibPosition(Model.Rect Rect)
        {
            if (Rect.getLeft() < 0 || Rect.getRight() > cvsMap.Width || Rect.getTop() < 0 || Rect.getBottom() > cvsMap.Height)
            {
                return false;
            }

            //Check if these are collistion with any item then stop create
            List<System.Windows.Point> points = getCollistionErea(Rect);
            foreach (System.Windows.Point point in points)
            {
                var result = VisualTreeHelper.HitTest(cvsMap, point);
                if (result != null && result.VisualHit is System.Windows.Shapes.Rectangle)
                {
                    return false;
                }
            }

            return true;
        }

        private void setRectanglePositionInCanvas(System.Windows.Shapes.Rectangle Rectangle, System.Windows.Point CurrentPoint, System.Windows.Point PrevPoint)
        {
            //Get delta
            double deltaX = CurrentPoint.X - PrevPoint.X;
            double deltaY = CurrentPoint.Y - PrevPoint.Y;

            //Get curent rectangle position
            double posX = Canvas.GetLeft(Rectangle);
            double postY = Canvas.GetTop(Rectangle);

            MapEditor.Model.Rect rect = new Model.Rect(posX + deltaX, postY + deltaY, Rectangle.Width, Rectangle.Height);

            ////move rectangle to delta position
            Canvas.SetLeft(Rectangle, posX + deltaX);
            Canvas.SetTop(Rectangle, postY + deltaY);

            if (CheckRectInvalibPosition(rect))
            {
                Canvas.SetLeft(Rectangle, posX + deltaX);
                Canvas.SetTop(Rectangle, postY + deltaY);
            }
            
        }

        /// <summary>
        /// Get current selected rectangle item in canvas
        /// </summary>
        /// <param name="point"></param>
        /// <returns>selected Rectangle</returns>
        private System.Windows.Shapes.Rectangle GetCurrentSelectedIlementInCanvas(System.Windows.Point point)
        {
            //Get current selected Item
            var result = VisualTreeHelper.HitTest(cvsMap, point);
            if (result != null)
            {
                var visualHit = result.VisualHit;
                if (visualHit == null)
                {
                    return null;
                }
                if (visualHit is System.Windows.Shapes.Rectangle)
                {
                    return visualHit as System.Windows.Shapes.Rectangle;
                }
            }

            return null;
        }

        #endregion


        private void SaveRTBAsPng(RenderTargetBitmap Bitmap, string FileName)
        {
            var enc = new PngBitmapEncoder();
            enc.Frames.Add(BitmapFrame.Create(Bitmap));

            using (var stm = File.Create(FileName))
            {
                enc.Save(stm);
            }
        }

        /// <summary>
        /// Delete item in map
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btndelete_Click(object sender, RoutedEventArgs e)
        {
            if (CurrentRectInMap != null)
            {
                deleteItem(CurrentRectInMap);
            }
        }

        /// <summary>
        /// Clear all item in map
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnClear_Click(object sender, RoutedEventArgs e)
        {
            if (cvsMap.Children.Count > 0)
            {
                removeItemsInMap();
            }
        }

        private bool removeItemsInMap()
        {
            if (cvsMap.Children.Count > 1)
            {
                if (MessageBox.Show("Do you want to clear all item in map", "Confirmation", MessageBoxButton.YesNo)
                                   == MessageBoxResult.Yes)
                {
                    //remove all item without cvsDragSelection  in map
                    int cvsIndex = cvsMap.Children.IndexOf(cvsDragSelection);
                    if (cvsIndex == 0)
                    {
                        cvsMap.Children.RemoveRange(1, cvsMap.Children.Count - 1);
                    }
                    else
                    {
                        cvsMap.Children.RemoveRange(0, cvsIndex - 1);
                        if (cvsMap.Children.Count > 1)
                        {
                            cvsMap.Children.RemoveRange(1, cvsMap.Children.Count - 1);
                        }

                    }

                    //Remove all object in list
                    if (MapObjects.Count > 0)
                    {
                        MapObjects.Clear();
                    }

                    return true;
                }
                else
                {
                    return false;
                }
            }


            return true;
        }

        /// <summary>
        /// Create multi item
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        private void CreateMultiItem(System.Windows.Point start, System.Windows.Point end)
        {
            int count = (int)((start.X + end.X) / SelectedGameItem.ImageObject.Width);
            System.Windows.Shapes.Rectangle rect = new System.Windows.Shapes.Rectangle();
            //double startY = Math.Abs(end.Y - start.Y);

            for (double i = start.X; i < end.X; i += SelectedGameItem.ImageObject.Width + 0.1)
            {
                rect = RectangleFactory.getDefaultRect(SelectedGameItem.ImageObject);
                AddRectangleToCanvas(rect,SelectedGameItem.Type ,new System.Windows.Point(i, end.Y));
                //Canvas.SetLeft(rect, i - rect.Width / 2);
                //Canvas.SetTop(rect, end.Y);
            }

        }

        private void InitDragSelectionRect(System.Windows.Point pt1, System.Windows.Point pt2)
        {
            updateSelectionRect(pt1, pt2);

            cvsDragSelection.Visibility = Visibility.Visible;
        }

        private void updateSelectionRect(System.Windows.Point p1, System.Windows.Point p2)
        {
            double x, y, width, height;
            x = p1.X;
            y = p1.Y;
            width = Math.Abs(p2.X - p1.X);
            height = Math.Abs(p2.Y - p1.Y);

            Canvas.SetLeft(BoderDragSelection, x);
            Canvas.SetTop(BoderDragSelection, y);
            BoderDragSelection.Width = width;
            BoderDragSelection.Height = height;
        }

        private void ApplyDragSelectionRect()
        {
            cvsDragSelection.Visibility = Visibility.Collapsed;

            double x = Canvas.GetLeft(BoderDragSelection);
            double y = Canvas.GetTop(BoderDragSelection);
            double width = BoderDragSelection.Width;
            double height = BoderDragSelection.Height;

            System.Windows.Rect dragRect = new System.Windows.Rect(x, y, width, height);
            dragRect.Inflate(width / 10, height / 10);

            CreateMultiItem(origMouseDownPoint, new System.Windows.Point(x + width, y));
        }

        private void deleteItem(System.Windows.Shapes.Rectangle Rect)
        {
            if (CurrentRectInMap != null)
            {
                //remove object in list
                foreach (MapGameItem item in MapObjects)
                {
                    if (item.Rectangle == Rect)
                    {
                        MapObjects.Remove(item);
                        break;
                    }
                }
                //remove rectangle in map
                cvsMap.Children.Remove(Rect);
            }
        }

        private List<System.Windows.Point> getCollistionErea(MapEditor.Model.Rect Rect)
        {
            List<System.Windows.Point> result = new List<System.Windows.Point>();
            result.Add(new System.Windows.Point(Rect.X, Rect.Y));
            result.Add(new System.Windows.Point(Rect.getLeft(), Rect.getTop()));
            result.Add(new System.Windows.Point(Rect.getLeft(), Rect.getBottom()));
            result.Add(new System.Windows.Point(Rect.getRight(), Rect.getTop()));
            result.Add(new System.Windows.Point(Rect.getRight(), Rect.getBottom()));

            return result;
        }

        /// <summary>
        /// Load map to edit
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnLoadMap_Click(object sender, RoutedEventArgs e)
        {
            //clear current items in map
            removeItemsInMap();
            var o = MapObjects;
            GameItemCollection items = new GameItemCollection();

            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Title = "Open Map file To Edit";
            dialog.Filter = "TXT files|*.txt";
            if ((bool)dialog.ShowDialog())
            {
                //Load data from mapfile
                string fistLine = string.Empty;
                using (StreamReader reader = File.OpenText(dialog.FileName))
                {
                    fistLine = reader.ReadLine();
                }
                //Get mapobjects from data
                if (!string.IsNullOrEmpty(fistLine))
                {
                    List<string> data = fistLine.Split(' ').ToList();
                    int objectCount = int.Parse(data[0].Trim());
                    int index = 1;

                    int id;
                    int type;
                    int limitLeft;
                    int limitRight;
                    double x, y, width, height;

                    for (int i = 0; i < objectCount; i++)
                    {
                        id = int.Parse(data[index++].Trim());
                        type = int.Parse(data[index++].Trim());
                        width = double.Parse(data[index++].Trim());
                        height = double.Parse(data[index++].Trim());
                        x = double.Parse(data[index++].Trim());
                        y = double.Parse(data[index++].Trim());
                        limitLeft = int.Parse(data[index++].Trim());
                        limitRight = int.Parse(data[index++].Trim());

                        System.Windows.Shapes.Rectangle itemRect = RectangleFactory.getDefaultRect(items.ToList().Find(obj => obj.Type == type).ImageObject);
                        MapGameItem item = new MapGameItem(type, itemRect);
                        //MapObjects.Add(item);
                        //draw object in map
                        AddRectangleToCanvas(itemRect,item.Type , new System.Windows.Point(x, y));
                    }

                }

            }
        }

        private void cbbMapNum_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (removeItemsInMap())
            {
                ((GameItemCollection)this.lGameItem.ItemsSource).UpdateGameItems(@"GameData\" + cbbMapNum.SelectedValue.ToString());
                lGameItem.SelectedIndex = 1;
            }
            else
            {
                e.Handled = false;
            }
        }

        private void createBridge(System.Windows.Point BridgeLeftPosition, int GameItemIndex)
        {
            Bitmap leftBridgeBitmap = ((GameItem)lGameItem.Items[GameItemIndex]).ImageObject;
            Bitmap centerBridgeBitmap = ((GameItem)lGameItem.Items[GameItemIndex + 1]).ImageObject;
            Bitmap rightBridgrBitmap = ((GameItem)lGameItem.Items[GameItemIndex + 2]).ImageObject;

            int centerBridgeType = ((GameItem)lGameItem.Items[GameItemIndex + 1]).Type;
            int rightBridgeType = ((GameItem)lGameItem.Items[GameItemIndex + 2]).Type;

            System.Windows.Shapes.Rectangle leftBridge = RectangleFactory.getDefaultRect(leftBridgeBitmap);
            System.Windows.Shapes.Rectangle centerBridge_1 = RectangleFactory.getDefaultRect(centerBridgeBitmap);
            System.Windows.Shapes.Rectangle centerBridge_2 = RectangleFactory.getDefaultRect(centerBridgeBitmap);
            System.Windows.Shapes.Rectangle rigtBridge = RectangleFactory.getDefaultRect(rightBridgrBitmap);
            //add bridge left
            AddRectangleToCanvas(leftBridge, SelectedGameItem.Type, BridgeLeftPosition);
            //add bridge center
            AddRectangleToCanvas(centerBridge_1, centerBridgeType, new System.Windows.Point(BridgeLeftPosition.X + leftBridgeBitmap.Width / 2 + centerBridgeBitmap.Width / 2 + objectDistance, BridgeLeftPosition.Y));
            AddRectangleToCanvas(centerBridge_2, centerBridgeType, new System.Windows.Point(BridgeLeftPosition.X + leftBridgeBitmap.Width / 2 + centerBridgeBitmap.Width / 2 * 3 + objectDistance, BridgeLeftPosition.Y));
            //add bridge right
            AddRectangleToCanvas(rigtBridge, rightBridgeType, new System.Windows.Point(BridgeLeftPosition.X + leftBridgeBitmap.Width / 2 + centerBridgeBitmap.Width * 2 + rightBridgrBitmap.Width / 2 + objectDistance, BridgeLeftPosition.Y));
        }

    }
}

//loadmap: check invalib map file

